About
Faraday synth is a four voice, two octave chromatic synthesizer. Synthesis is carried out by a miniature arduino board that feeds a one watt amplifier and two inch speaker. There are four buttons that play notes, four knobs that control pitch, a latch button, a volume knob, and a power button. The enclosure is machined cast aluminum with a hand etched circuit board.

The synthesis algorithm is quite simple in order to run on the arduino, and consists of four one-bit oscillators that are updated by an interrupt service routine running at 50 kHz. Each oscillator's frequency is determined by counting the number of interrupts (i.e. samples) per half wave cycle, then toggling the oscillator's waveform state high or low. Interaction is handled within the main program loop where button states are read, potentiometer values are read and low pass filtered, and state variables are set.

Faraday synth was built as a present for my son on his third birthday.

Photos
Circuitry
Code
//------------------------------------------------------------------------------
// Faraday Synthesizer
//
// Interrupt driven 1-bit four-voice synthesis for arduino + atmega328 @ 16MHz
// Written for Faraday Moses Lin-Baker's 3rd Birthday (01/08/14)
//
// I Love You Faraday!
//
// Cooper Baker - 11/29/13
//------------------------------------------------------------------------------


// Includes
//------------------------------------------------------------------------------
#include "main.h"


// Definitions
//------------------------------------------------------------------------------
//2MHz for /8 prescale from 16MHz
#define TIMER_FREQ 2000000.0

// digital pins
#define SPEAKER_PIN 2

// led pins
#define LED_1_PIN   3
#define LED_2_PIN   4
#define LED_3_PIN   5
#define LED_4_PIN   6
#define LED_5_PIN   7

// switch pins
#define SW_1_PIN    8
#define SW_2_PIN    9
#define SW_3_PIN    10
#define SW_4_PIN    11
#define SW_5_PIN    12

// analog pins
#define POT_1_PIN   3
#define POT_2_PIN   2
#define POT_3_PIN   1
#define POT_4_PIN   0

// isr clock rate ( i.e. sample rate )
#define SAMPLE_RATE 50000

// clipping macro
#define clip( x, l, h ) (x) < (l) ? (l) : ( (x) > (h) ? (h) : (x) )


//------------------------------------------------------------------------------
// Variables
//------------------------------------------------------------------------------
// half-cycle-samples lookup table
// half-cycle-samples = ( sample-rate / cycles-per-second ) / 2.0
// 55Hz to 440Hz
int freqArray[ 1024 ] = { 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57 };

// potentiometer values
int pot1 = 0;
int pot2 = 0;
int pot3 = 0;
int pot4 = 0;

// oscillator states
byte osc1 = 0;
byte osc2 = 0;
byte osc3 = 0;
byte osc4 = 0;

// gated oscillator states
byte out1 = 0;
byte out2 = 0;
byte out3 = 0;
byte out4 = 0;

// switch states
byte sw1 = 0;
byte sw2 = 0;
byte sw3 = 0;
byte sw4 = 0;
byte sw5 = 0;

// switch states history
byte sw1Prev = 0;
byte sw2Prev = 0;
byte sw3Prev = 0;
byte sw4Prev = 0;
byte sw5Prev = 0;

// gate/latch states
byte gate1 = 0;
byte gate2 = 0;
byte gate3 = 0;
byte gate4 = 0;
byte latch = 0;

// mixed gated oscillators
byte outMix = 0;

// oscillator half-cycle counters
int inc1 = 0;
int inc2 = 0;
int inc3 = 0;
int inc4 = 0;

// output byte for PORTD register
byte output;

// interrupt timer variables
unsigned char clock_cycles = 0;
unsigned int  isr_latency  = 0;


//------------------------------------------------------------------------------
// Prototypes
//------------------------------------------------------------------------------
void setup( void );
void loop( void );
void setup_timer_2( float clock_frequency );
void hello( void );


//------------------------------------------------------------------------------
// ISR - interrupt service routine for TIMER2_OVF_vect ( timer 2 overflow )
//       updates, gates, mixes, and outputs oscillator signals
//------------------------------------------------------------------------------
ISR( TIMER2_OVF_vect )
{
    // gate and mix oscillators into output byte
    output = ( osc1 & gate1 ) || ( osc2 & gate2 ) || ( osc3 & gate3 ) || ( osc4 & gate4 );
   
    // shift output signal to speaker pin and bitmask into PORTD register
    PORTD = PIND | ( output << SPEAKER_PIN );
   
    // half-cycle counter wrapping
    if( ++inc1 >= freqArray[ pot1 ] )
    {
        osc1 = !osc1;
        inc1 = 0;
    }
    else
    {
        osc1 = osc1;
        inc1 = inc1;
    }

    if( ++inc2 >= freqArray[ pot2 ] )
    {
        osc2 = !osc2;
        inc2 = 0;
    }
    else
    {
        osc2 = osc2;
        inc2 = inc2;
    }
   
    if( ++inc3 >= freqArray[ pot3 ] )
    {
        osc3 = !osc3;
        inc3 = 0;
    }
    else
    {
        osc3 = osc3;
        inc3 = inc3;
    }
   
    if( ++inc4 >= freqArray[ pot4 ] )
    {
        osc4 = !osc4;
        inc4 = 0;
    }
    else
    {
        osc4 = osc4;
        inc4 = inc4;
    }

    // store current timer value for latency correction
    isr_latency = TCNT2;

    // reload the timer and correct for latency
    TCNT2 = isr_latency + clock_cycles;
}


//------------------------------------------------------------------------------
// setup_timer_2 - sets up frequency of timer 2 overflow
//------------------------------------------------------------------------------
void setup_timer_2( float clock_frequency )
{
    // calculate overflow count value
    clock_cycles = (int)( ( 257.0 - ( (float)TIMER_FREQ / clock_frequency ) ) + 0.5 );

    // timer 2 settings: timer prescaler /8,  
    TCCR2A = 0;
    TCCR2B = 0 << CS22 | 1 << CS21 | 0 << CS20;
   
    // timer 2 overflow interrupt enable  
    TIMSK2 = 1 << TOIE2;    
   
    // timer 2 overflow count
    TCNT2 = clock_cycles;
}


//------------------------------------------------------------------------------
// hello - flashes lights and plays a note
//------------------------------------------------------------------------------
void hello( void )
{
    // select and turn on a note
    pot1  = 960;
    gate1 = HIGH;

    // turn on the lights
    digitalWrite( LED_1_PIN, HIGH );
    digitalWrite( LED_2_PIN, HIGH );
    digitalWrite( LED_3_PIN, HIGH );
    digitalWrite( LED_4_PIN, HIGH );
    digitalWrite( LED_5_PIN, HIGH );

    // wait a moment
    delay( 10 );
   
    // turn off the note
    gate1 = LOW;
   
    // wait a while
    delay( 90 );
   
    // turn off the lights
    digitalWrite( LED_1_PIN, LOW );
    digitalWrite( LED_2_PIN, LOW );
    digitalWrite( LED_3_PIN, LOW );
    digitalWrite( LED_4_PIN, LOW );
    digitalWrite( LED_5_PIN, LOW );
}


//------------------------------------------------------------------------------
// setup - initializes the microcontroller
//------------------------------------------------------------------------------
void setup( void )
{
    // turn off the built-in led
    pinMode     ( 13, OUTPUT );
    digitalWrite( 13, LOW    );

    // input pin initializations
    pinMode( SW_1_PIN, INPUT_PULLUP );
    pinMode( SW_2_PIN, INPUT_PULLUP );
    pinMode( SW_3_PIN, INPUT_PULLUP );
    pinMode( SW_4_PIN, INPUT_PULLUP );
    pinMode( SW_5_PIN, INPUT_PULLUP );
 
    // output pin initializations
    pinMode( LED_1_PIN,   OUTPUT );
    pinMode( LED_2_PIN,   OUTPUT );
    pinMode( LED_3_PIN,   OUTPUT );
    pinMode( LED_4_PIN,   OUTPUT );
    pinMode( LED_5_PIN,   OUTPUT );

    // speaker pin initialization
    pinMode( SPEAKER_PIN, INPUT_PULLUP );

    // interrupt timer initialization
    setup_timer_2( SAMPLE_RATE );
   
    // say hello
    hello();
}


//------------------------------------------------------------------------------
// loop - the main program loop
//------------------------------------------------------------------------------
void loop( void )
{
    // read and low-pass filter the potentiometers
    pot1 = ( analogRead( POT_1_PIN ) * 0.09 ) + ( pot1 * 0.91 );
    pot2 = ( analogRead( POT_2_PIN ) * 0.09 ) + ( pot2 * 0.91 );
    pot3 = ( analogRead( POT_3_PIN ) * 0.09 ) + ( pot3 * 0.91 );
    pot4 = ( analogRead( POT_4_PIN ) * 0.09 ) + ( pot4 * 0.91 );
   
    // clip off noise at the ends of the potentiometers' ranges
    pot1 = clip( pot1, 64, 960 );
    pot2 = clip( pot2, 64, 960 );
    pot3 = clip( pot3, 64, 960 );
    pot4 = clip( pot4, 64, 960 );
   
    // read the switches
    sw1 = ! digitalRead( SW_1_PIN );
    sw2 = ! digitalRead( SW_2_PIN );
    sw3 = ! digitalRead( SW_3_PIN );
    sw4 = ! digitalRead( SW_4_PIN );
    sw5 = ! digitalRead( SW_5_PIN );

    // toggle latch when switch 5 goes high
    if( ( sw5 == HIGH ) && ( sw5Prev == LOW ) )
    {
        latch = !latch;
    }

    if( latch )
    {
        // if latch is on toggle gates when switches go high
        if( ( sw1 == HIGH ) && ( sw1Prev == LOW ) )
        {
            gate1 = !gate1;
        }

        if( ( sw2 == HIGH ) && ( sw2Prev == LOW ) )
        {
            gate2 = !gate2;
        }

        if( ( sw3 == HIGH ) && ( sw3Prev == LOW ) )
        {
            gate3 = !gate3;
        }

        if( ( sw4 == HIGH ) && ( sw4Prev == LOW ) )
        {
            gate4 = !gate4;
        }
    }
    else
    {
        // if latch is off set gates to switch states
        gate1 = sw1;
        gate2 = sw2;
        gate3 = sw3;
        gate4 = sw4;
    }

    // switch history
    sw1Prev = sw1;
    sw2Prev = sw2;
    sw3Prev = sw3;
    sw4Prev = sw4;
    sw5Prev = sw5;
   
    // update leds according to gate/latch values
    digitalWrite( LED_1_PIN, gate1 );
    digitalWrite( LED_2_PIN, gate2 );
    digitalWrite( LED_3_PIN, gate3 );
    digitalWrite( LED_4_PIN, gate4 );
    digitalWrite( LED_5_PIN, latch );
}


//------------------------------------------------------------------------------
// main - entry point for microcontroller config and operation
//------------------------------------------------------------------------------
int main(void)
{
    init();
   
    setup();
   
    spin:
        loop();
    goto spin;
   
    return 0;
}


//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------